#include "stm32f10x.h"                  // Device header
#include <time.h>
#include "MyRTC.h"

uint16_t MyRTC_Time[6] = {2023, 1, 1, 23, 59, 55};


/*
 * @brief 初始化 RTC 时钟
 * 
 */
void MyRTC_Init(void)
{
	// 1. 开启 PWR 和 BKP 时钟。
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP, ENABLE);
	
	// 2. 使能 BKP 和 RTC 的访问。
	PWR_BackupAccessCmd(ENABLE);
	
	// 检测到 BKP 当中没有指定数据的时候表示后备电源首次供电, 进行寄存器和时钟的初始化
	if(BKP_ReadBackupRegister(BKP_DR1) != 0xA5A5)
	{
		// 3. 开启 LSE 时钟，并等待 LSE 时钟启动完成并稳定下来
		RCC_LSEConfig(RCC_LSE_ON);
		while(RCC_GetFlagStatus(RCC_FLAG_LSERDY) != SET)
		;
		
		// 4. 选择RTC时钟源头，配置 RTCCLK 这个数据选择，指定 LSE 为 RTCCLK。
		RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
		RCC_RTCCLKCmd(ENABLE);
		
		// 5. 等待1：软件等待 RTC_CRL 寄存器中的 RSF 位（寄存器同步标志）被硬件置1。
		RTC_WaitForSynchro();
		
		// 6. 等待2：通过查询 RTC_CR 寄存器中的 RTOFF 状态位，判断 RTC 寄存器是否处于更新，仅当 RTOFF 状态位 是 1 时，才可以写入 RTC 寄存器。
		RTC_WaitForLastTask();
		
		// 7. 配置预分频器, 并等待 RTC 写入结束
		RTC_SetPrescaler(32768 - 1);
		RTC_WaitForLastTask();
		
		// 8. 配置 CNT 的值，给 RTC 一个初始时间，并写入 BKP 当中一个值
		MyRTC_SetTime();
		BKP_WriteBackupRegister(BKP_DR1, 0xA5A5);
	}
	else
	{
		// 以防 Init 函数掉完, 有其他函数修改 RTC 寄存器, 因此这里再添加下等待.
		RTC_WaitForSynchro();
		RTC_WaitForLastTask();
	}
}


/*
 * @brief 将数组内时间设置到 RTC
 * 
 */
void MyRTC_SetTime(void)
{
	time_t time_cnt;
	struct tm time_date;
	
	// 将数组时间填充到 time_date
	time_date.tm_year = MyRTC_Time[0] - 1900;
	time_date.tm_mon  = MyRTC_Time[1] - 1;
	time_date.tm_mday = MyRTC_Time[2];
	time_date.tm_hour = MyRTC_Time[3];
	time_date.tm_min  = MyRTC_Time[4];
	time_date.tm_sec  = MyRTC_Time[5];
	
	// time_date 结构体转换为时间戳
	time_cnt = mktime(&time_date);
	
	// 设置时间到 RTC 中
	RTC_SetCounter(time_cnt);
	RTC_WaitForLastTask();
}


/*
 * @brief 读取时间到数组
 * 
 */
void MyRTC_ReadTime(void)
{
	time_t time_cnt;
	struct tm *time_date;
	
	// 获取 RTC 中时间戳
	time_cnt = RTC_GetCounter();
	
	// 基于时间戳进行时区的转换: 东八区加上8个小时
	time_cnt += 28800;
	
	// 时间戳转换为时间结构体
	time_date = localtime(&time_cnt);
	
	MyRTC_Time[0] = time_date->tm_year + 1900;
	MyRTC_Time[1] = time_date->tm_mon + 1;
	MyRTC_Time[2] = time_date->tm_mday;
	MyRTC_Time[3] = time_date->tm_hour;
	MyRTC_Time[4] = time_date->tm_min;
	MyRTC_Time[5] = time_date->tm_sec;
}